/*
 * Copyright (c) 2011. c.mueller
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * The Software shall be used for Good, not Evil.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package de.altitude.cimuelle.coop;

import de.altitude.cimuelle.Config;
import de.altitude.cimuelle.db.DatabaseCoopData;
import de.altitude.cimuelle.db.DatabaseManager;
import de.altitude.cimuelle.enums.Perk;
import de.altitude.cimuelle.enums.Plane;
import de.altitude.cimuelle.utils.ServerOperations;
import de.altitudecustomcommands.Player;
import de.altitudecustomcommands.playerHandler;
import de.altitudecustomcommands.virtualServer;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;

/**
 * Class description:
 * <p/>
 * User: cybernaut
 * Date: 06.10.11
 * Time: 23:31
 */
public class CoopDataHandler {
    final private static Logger LOGGER = Logger.getLogger("CoopDataHandler");

    private static final List<CoopGame> coopGamesList = new ArrayList<CoopGame>();

    private static final String SBASEDESTROYER = "baseDestroyer";
    private static final String SBASEDESTROYEDALIVE = "baseDestroyedAlive";
    private static final String SDEAD = "dead";
    private static final String SMAPWONPLAYERBONUS = "mapWonPlayerBonus";
    private static final String STURRETKILLS = "turretKills";
    private static final String SBASEDESTROYEDSPLITPOINTS = "baseDestroyedSplitPoints";
    private static final String SRANKPOINTS = "rankPoints";
    private static final String SMINIMUMALIVETIME = "minimumAliveTime";

    public static CoopGame newCoopGame(String port, virtualServer serverByPort) {
        CoopGame coopGame = new CoopGame(port, serverByPort);
        coopGamesList.add(coopGame);
        return coopGame;
    }

    public static void addPlayerToTeam(String port, Player player) {
        for (CoopGame coopGame : coopGamesList) {
            if (coopGame.isActive() && port.equals(coopGame.getPort()) && !coopGame.isPlayerOnTeam(player)) {
                LOGGER.debug(port + ":Adding player to CoopGame" + coopGame);
                if (!player.playerIsBot()) {
                    coopGame.addPlayerToTeam(player);
                    coopGame.addPlane(player.getPlayerConfiguration().plane);
                }
            }
        }
    }

    public static void endCoopGame(String port, playerHandler playerList, int winningTeam, double timeSeconds) {
        CoopGame coopGame = getCoopGameByPort(port);
        if (coopGame != null) {
            coopGame.setActive(false);
            coopGame.setWinningTeam(winningTeam);
            coopGame.setDuration(timeSeconds);
            coopGame.setStop(new Timestamp(coopGame.getStart().getTime() + (long) timeSeconds));
            coopGame.setCoopRank();
            calculatePoints(coopGame, playerList);
            coopGame.saveData();
            removeCoopGameFromList(coopGame);
        }
    }

    private static void removeCoopGameFromList(CoopGame coopGame) {
        coopGamesList.remove(coopGame);
    }

    private static void calculatePoints(CoopGame coopGame, playerHandler playerList) {
        HashMap<String, Integer> pointsInfo = new HashMap<String, Integer>();

        //TODO read points multiplicator from config
        Properties coopConfig = null;
        try {
            coopConfig = Config.getInstance().getCoopConfig(coopGame.getPort(), coopGame.getMap().getMap());
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (coopConfig == null) {
            LOGGER.info(coopGame.getPort() + " : No extra configuration for map " + coopGame.getMap().getMap() + " found.");
            return;
        }
        // TODO leaving will be punished ?
        //int playerLeft = Integer.parseInt(coopConfig.getProperty("playerLeft", "0"));

        int topRankPoints = 0;
        String rankPoints = coopConfig.getProperty(SRANKPOINTS, "0");

        StringTokenizer stringTokenizer = new StringTokenizer(rankPoints, ",");
        for (int i = 1; i <= stringTokenizer.countTokens(); i++) {
            String rankPoint = stringTokenizer.nextToken();
            if (i == coopGame.getRank()) {
                topRankPoints = Integer.parseInt(rankPoint);
                pointsInfo.put(SRANKPOINTS, topRankPoints);
            }
        }

        if (coopGame.getBaseDestroyedBy() != null && !"".equals(coopGame.getBaseDestroyedBy())) {
            coopGame.getCoopPlayer(coopGame.getBaseDestroyedBy());
            DatabaseManager.getInstance().increaseCoopPoints(coopGame.getPort(), coopGame.getBaseDestroyedBy(), 10);
        }

        int splitPoints = 0;
        if (coopGame.getLeftTeamPlayersAsList().size() > 0) {
            //int playerPoints = Integer.parseInt(coopConfig.getProperty("playerPoints", "0"));
            if (coopGame.isBaseDestroyed()) {
                int baseDestroyedSplitPoints = Integer.parseInt(coopConfig.getProperty(SBASEDESTROYEDSPLITPOINTS, "0"));
                pointsInfo.put(SBASEDESTROYEDSPLITPOINTS, baseDestroyedSplitPoints);
                splitPoints += baseDestroyedSplitPoints / coopGame.getLeftTeamPlayersAsList().size();
            }
            //splitPoints += playerPoints / coopGame.getLeftTeamPlayersAsList().size();
        }

        int turretKills = Integer.parseInt(coopConfig.getProperty(STURRETKILLS, "1"));
        int baseDestroyer = Integer.parseInt(coopConfig.getProperty(SBASEDESTROYER, "0"));
        int baseDestroyedAlive = Integer.parseInt(coopConfig.getProperty(SBASEDESTROYEDALIVE, "0"));
        int dead = Integer.parseInt(coopConfig.getProperty(SDEAD, "0"));
        int mapWonBonus = Integer.parseInt(coopConfig.getProperty(SMAPWONPLAYERBONUS, "0"));

        int minimumAliveTime = Integer.parseInt(coopConfig.getProperty(SMINIMUMALIVETIME, "0"));

        for (CoopPlayer cp : coopGame.getLeftTeamPlayersAsList()) {
            StringBuilder pointsBy = new StringBuilder();

            double mulitplier = Double.parseDouble(coopConfig.getProperty(cp.getPlayerConfiguration().plane.toString().toLowerCase(), "1"));
            double planeConfMuli = calcPlaneConigurationMultiplier(cp, coopConfig);
            mulitplier += planeConfMuli;
            pointsBy.append("Planeconfiguration multiplier: ").append(planeConfMuli).append("\n");
            //pointsInfo.put("planeConfiguration", (int) planeConfMuli * 10);
            if (mulitplier < 0.1) {
                mulitplier = 0.1;
            }

            // adding turretKills
            calcSetPoints(STURRETKILLS, cp.getTurretKills() * turretKills, mulitplier, pointsInfo, cp, pointsBy);

            // if player is still alive in the end, add points
            if (!cp.isAlive() || cp.isLeftBeforeDie()) {
                calcNegSetPoints(SDEAD, dead, 1, pointsInfo, cp, pointsBy);
            }

            if (cp.isAlive() || (!cp.isLeftBeforeDie() && cp.getDiedAfterSeconds() > -1 && minimumAliveTime < cp.getDiedAfterSeconds())) {
                // if base destroyed add points
                if (cp.isDestroyedBase()) {
                    calcSetPoints(SBASEDESTROYER, baseDestroyer, mulitplier, pointsInfo, cp, pointsBy);
                    if (cp.isAlive()) {
                        calcSetPoints(SBASEDESTROYEDALIVE, baseDestroyedAlive, mulitplier, pointsInfo, cp, pointsBy);
                    }
                }

                if (coopGame.isBaseDestroyed()) {
                    // adding split points (calculated)
                    calcSetPoints("SumSplitPoints", splitPoints, mulitplier, pointsInfo, cp, pointsBy);

                    // adding points for achieving top places
                    calcSetPoints(SRANKPOINTS, topRankPoints, 1, pointsInfo, cp, pointsBy);

                    // adding mapWonBonus
                    calcSetPoints(SMAPWONPLAYERBONUS, mapWonBonus, mulitplier, pointsInfo, cp, pointsBy);
                }

            } else {
                pointsInfo.put(SMINIMUMALIVETIME, minimumAliveTime);
            }
            // using multiplier
            int coopPoints = cp.getCoopPoints();
            //pointsBy.append("points multiplied by: ").append(mulitplier).append(" = ").append(coopPoints).append("\n");
            //cp.setCoopPoints(coopPoints);
            LOGGER.info(coopGame.getPort() + ":" + coopGame.getDatabaseId() + ": Player " + cp.getName() + " got the following points: \n" + pointsBy.toString());
            LOGGER.info(coopGame.getPort() + ":" + coopGame.getDatabaseId() + ": Player " + cp.getName() + " got the following points: " + coopPoints);

            if ("true".equals(coopConfig.getProperty("tellPlayerScore", "false"))) {
                StringBuilder sb = new StringBuilder();
                sb.append("Point calculation:");
                for (String key : pointsInfo.keySet()) {
                    if (pointsInfo.get(key) != 0) {
                        sb.append(key).append(":").append(pointsInfo.get(key)).append("|");
                    }
                }
                sb.append("ScoreMultiplier:").append(mulitplier);
                ServerOperations.sendServerWhisper(coopGame.getPort(), cp.getName(), sb.toString());
            }
        }
    }

    private static double calcPlaneConigurationMultiplier(CoopPlayer cp, Properties coopConfig) {
        double varMultiplier = 0;

        Perk perkRed = cp.getPlayerConfiguration().perkRed;
        Perk perkBlue = cp.getPlayerConfiguration().perkBlue;
        Perk perkGreen = cp.getPlayerConfiguration().perkGreen;

        Plane plane = cp.getPlayerConfiguration().plane;

        String[] configs = new String[]
                {
                        plane + "_" + perkRed.toString(),
                        plane + "_" + perkGreen.toString(),
                        plane + "_" + perkBlue.toString()
                };

        for (String configName : configs) {
            final double perkMultiplier = Double.parseDouble(coopConfig.getProperty(configName.toLowerCase().replace(" ", "_"), "0"));
            varMultiplier += perkMultiplier;
            LOGGER.info("Plane configuration multiplier: " + configName + "=" + perkMultiplier);
        }
        return varMultiplier;
    }

    private static void calcSetPoints(String field, int value, double mulitplier, HashMap<String, Integer> pointsInfo, CoopPlayer cp, StringBuilder sb) {
        final int calcLocal = (int) (value * mulitplier);
        sb.append(field).append(": ").append(calcLocal).append("\n");
        cp.addCoopPts(calcLocal);
        pointsInfo.put(field, calcLocal);
    }

    private static void calcNegSetPoints(String field, int value, double mulitplier, HashMap<String, Integer> pointsInfo, CoopPlayer cp, StringBuilder sb) {
        final int calcLocal = (int) (value / mulitplier);
        sb.append(field).append(": ").append(calcLocal).append("\n");
        cp.addCoopPts(calcLocal);
        pointsInfo.put(field, calcLocal);
    }


    private static CoopGame getCoopGameByPort(String port) {
        if (!Config.getInstance().isGetConfigByPort(port, "coopRankingActive", "false")) {
            return null;
        } else {
            CoopGame toReturn = null;
            for (CoopGame coopGame : coopGamesList) {
                if (coopGame.isActive() && port.equals(coopGame.getPort()) && coopGame.getDatabaseId() > -1) {
                    if (toReturn == null || toReturn.getDatabaseId() < coopGame.getDatabaseId()) {
                        toReturn = coopGame;
                    }
                }
            }
            return toReturn;
        }
    }

    public static void updateGameStatsKill(String port, Player victim) {
        CoopGame coopGame = getCoopGameByPort(port);
        if (coopGame != null && !coopGame.isBaseDestroyed() && !coopGame.isChargePlanted()) {
            coopGame.getGameStats().setPlayerDied(coopGame.getGameStats().getPlayerDied() + 1);
            coopGame.getGameStats().setActivePlayer(coopGame.getGameStats().getActivePlayer() - 1);
            coopGame.setPlayerAlive(victim, false, coopGame);
        }
    }

    public static void updateGameStatsTurretDamage(String port, int damage) {
        CoopGame coopGame = getCoopGameByPort(port);
        if (coopGame != null) {
            coopGame.getGameStats().setTurretsDamageDone(coopGame.getGameStats().getTurretsDamageDone() + damage);
        }
    }

    public static void updateGameStatsTurretKill(String port, Player player) {
        CoopGame coopGame = getCoopGameByPort(port);
        if (coopGame != null) {
            coopGame.getGameStats().setTurretsDestroyed(coopGame.getGameStats().getTurretsDestroyed() + 1);
            CoopPlayer cp = coopGame.getCoopPlayer(player);
            if (cp != null) {
                cp.addTurretKill();
            } else {
                LOGGER.error("Player " + player + " not found.");
            }
        }
    }

    public static void updateBaseDestroyed(String port, Player player) {
        CoopGame coopGame = getCoopGameByPort(port);
        if (coopGame != null) {
            coopGame.setBaseDestroyedBy(player);
            coopGame.setBaseDestroyed(true);
        }
    }

    public static void updateGamePlayerStatsIsAlive(Player victim, boolean alive) {
        CoopGame coopGame = getCoopGameByPort(victim.getPort());
        if (coopGame != null) {
            coopGame.setPlayerAlive(victim, alive);
        }
    }

    public static CoopGame getActiveGame(String port) {
        return getCoopGameByPort(port);
    }

    public static CoopGame getGameRankById(int databaseId) {
        return DatabaseCoopData.getCoopGameById(databaseId);
    }

    public static List<CoopGame> getGameRanksByMap(String port, String map) {
        return DatabaseCoopData.getCoopGameRanksByPortMap(port, map);
    }

    public static int getGameRankByCoopGame(CoopGame coopGame) {
        CoopGame game = DatabaseCoopData.getCoopGameRanksByPortMapId(coopGame.getPort(), coopGame.getMap().getMap(), coopGame.getDatabaseId());
        return game.getRank();
    }

    public static List<CoopGame> getGameRanksByGame(CoopGame coopGame) {
        List<CoopGame> games = DatabaseCoopData.getCoopGameRanksByPortMap(coopGame.getPort(), coopGame.getMap().getMap());
        List<CoopGame> returnGames = new ArrayList<CoopGame>();

        for (CoopGame game : games) {
            if ((coopGame.getRank() - game.getRank() < 2) && (coopGame.getRank() - game.getRank() > -2)) {
                returnGames.add(game);
            }
        }
        return returnGames;
    }

    public static void deleteCoopGame(CoopGame coopGame) {
        int databaseId = coopGame.getDatabaseId();
        if (databaseId > -1) {
            DatabaseCoopData.deleteCoopGame(databaseId);
        }
        removeCoopGameFromList(coopGame);
    }
}